#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#include <asm/io.h>
#include <asm/uaccess.h>

#include "fs4412_led.h" //cmd参数的宏定义

MODULE_LICENSE("Dual BSD/GPL"); // 版权开源

#define LED_MA 500
#define LED_MI 0
#define LED_NUM 1

/* 定义寄存器指针变量*/
static unsigned int *gpm1con;
static unsigned int *gpm1dat;
static unsigned int *gpm2con;
static unsigned int *gpm2dat;
static unsigned int *gpm3con;
static unsigned int *gpm3dat;
static unsigned int *gpm4con;
static unsigned int *gpm4dat;

/* 定义结构体变量*/
struct cdev cdev;

/* 点亮LED灯*/
void fs4412_led_on(int nr)
{
	switch (nr)
	{
	case 1:
		writel(readl(gpm4dat) & ~(1 << 2), gpm4dat);
		break;
		// 请同学们自己实现剩下几个灯的点亮
	case 2:
                writel(readl(gpm1dat) & ~(1 << 6), gpm1dat);
		break;
	case 3:
                writel(readl(gpm3dat) & ~(1 << 5), gpm3dat);
		break;
	case 4:
                writel(readl(gpm1dat) & ~(1 << 3), gpm1dat);
		break;
	case 5:
                writel(readl(gpm2dat) & ~(1 << 2), gpm2dat);
		break;
	default:
		break;
	}
}

/* 熄灭LED灯*/
void fs4412_led_off(int nr)
{
	// GPM4_2 GPM1_6 GPM3_5 GPM1_3 GPM2_2
	switch (nr)
	{
	case 1:
		writel(readl(gpm4dat) | (1 << 2), gpm4dat);
		break;
		// 请同学们自己实现剩下几个灯的熄灭
	case 2:
                writel(readl(gpm1dat) | (1 << 6), gpm1dat);
		break;
	case 3:
                writel(readl(gpm3dat) | (1 << 5), gpm3dat);
		break;
	case 4:
                writel(readl(gpm1dat) | (1 << 3), gpm1dat);
		break;
	case 5:
                writel(readl(gpm2dat) | (1 << 2), gpm2dat);
		break;
	default:
		break;
	}
}

/* 与应用程序对应，对应OPEN*/
static int fs4412_led_open(struct inode *inode, struct file *file)
{
	return 0;
}

/* 与应用程序对应，对应退出（Ctrl+C）*/
static int fs4412_led_release(struct inode *inode, struct file *file)
{
	return 0;
}

/* 与应用程序对应，对应ioctl*/
static long fs4412_led_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int nr;
 
	if (copy_from_user((void *)&nr, (void *)arg, sizeof(nr)))
		return -EFAULT;

	if (nr < 1 || nr > 5)
		return -EINVAL;

	switch (cmd)
	{
	case LED_ON:
		fs4412_led_on(nr);
		break;
	case LED_OFF:
		fs4412_led_off(nr);
		break;
	default:
		printk("Invalid argument");
		return -EINVAL;
	}

	return 0;
}

/* I/O映射，物理地址映射为虚拟地址*/
int fs4412_led_ioremap(void)
{
	int ret;

	gpm1con = ioremap(FS4412_GPM1CON, 4);
	if (gpm1con == NULL)
	{
		printk("ioremap gpm1con\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm1dat = ioremap(FS4412_GPM1DAT, 4);
	if (gpm1dat == NULL)
	{
		printk("ioremap gpm1dat\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm2con = ioremap(FS4412_GPM2CON, 4);
	if (gpm2con == NULL)
	{
		printk("ioremap gpm2con\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm2dat = ioremap(FS4412_GPM2DAT, 4);
	if (gpm2dat == NULL)
	{
		printk("ioremap gpm2dat\n");
		ret = -ENOMEM;
		return ret;
	}
	gpm3con = ioremap(FS4412_GPM3CON, 4);
	if (gpm3con == NULL)
	{
		printk("ioremap gpm3con\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm3dat = ioremap(FS4412_GPM3DAT, 4);
	if (gpm3dat == NULL)
	{
		printk("ioremap gpm3dat\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm4con = ioremap(FS4412_GPM4CON, 4);
	if (gpm4con == NULL)
	{
		printk("ioremap gpm4con\n");
		ret = -ENOMEM;
		return ret;
	}

	gpm4dat = ioremap(FS4412_GPM4DAT, 4);
	if (gpm4dat == NULL)
	{
		printk("ioremap gpm4dat\n");
		ret = -ENOMEM;
		return ret;
	}

	return 0;
}

/* 释放映射*/
void fs4412_led_iounmap(void)
{
	iounmap(gpm1con);
	iounmap(gpm1dat);
	iounmap(gpm2con);
	iounmap(gpm2dat);
	iounmap(gpm3con);
	iounmap(gpm3dat);
	iounmap(gpm4con);
	iounmap(gpm4dat);
}

/* I/O初始化*/
void fs4412_led_io_init(void)
{
	//	GPM4_2 GPM1_6 GPM3_5 GPM1_3 GPM2_2

	writel((readl(gpm4con) & ~(0xf << 8)) | (0x1 << 8), gpm4con);
	writel(readl(gpm4dat) | (0x1 << 2), gpm4dat);
	// 请同学们自己实现其它几个灯的初始化  设置控制位为输出，并将灯熄灭

        writel((readl(gpm1con) & ~(0xf << 24)) | (0x1 << 24), gpm1con);
        writel(readl(gpm1dat) | (0x1 << 6), gpm1dat);

        writel((readl(gpm3con) & ~(0xf << 20)) | (0x1 << 20), gpm3con);
        writel(readl(gpm3dat) | (0x1 << 5), gpm3dat);

        writel((readl(gpm1con) & ~(0xf << 12)) | (0x1 << 12), gpm1con);
        writel(readl(gpm1dat) | (0x1 << 3), gpm1dat);

        writel((readl(gpm2con) & ~(0xf << 8)) | (0x1 << 8), gpm2con);
        writel(readl(gpm2dat) | (0x1 << 2), gpm2dat);
	
}

/* 定位文件操作函数*/
struct file_operations fs4412_led_fops = {
	.owner = THIS_MODULE,
	.open = fs4412_led_open,
	.release = fs4412_led_release,
	.unlocked_ioctl = fs4412_led_unlocked_ioctl,
};

/* 创建字符设备，进行点灯初始化*/
static int fs4412_led_init(void)
{
	dev_t devno = MKDEV(LED_MA, LED_MI);
	int ret;

	ret = register_chrdev_region(devno, LED_NUM, "newled"); // 注册字符设备
	if (ret < 0)
	{
		printk("register_chrdev_region\n");
		return ret;
	}

	// 注册文件操作字符集
	cdev_init(&cdev, &fs4412_led_fops);
	cdev.owner = THIS_MODULE;
	ret = cdev_add(&cdev, devno, LED_NUM);
	if (ret < 0)
	{
		printk("cdev_add\n");
		goto err1;
	}

	ret = fs4412_led_ioremap(); // 点灯映射
	if (ret < 0)
		goto err2;

	fs4412_led_io_init();

	printk("Led init\n");

	return 0;
err2:
	cdev_del(&cdev); // 释放操作
err1:
	unregister_chrdev_region(devno, LED_NUM); // 释放操作
	return ret;
}

static void fs4412_led_exit(void) // 资源释放函数
{
	dev_t devno = MKDEV(LED_MA, LED_MI);

	fs4412_led_iounmap();
	cdev_del(&cdev);
	unregister_chrdev_region(devno, LED_NUM);
	printk("Led exit\n");
}

module_init(fs4412_led_init);
module_exit(fs4412_led_exit);
